﻿using System.Collections.Generic;
using Batman.Core;
using IdentityServer4.EntityFramework.DbContexts;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using System.Linq;
using System.Threading.Tasks;
using IdentityServer4.EntityFramework.Entities;


namespace Batman.Web.Api.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class ApiResourcesController : ControllerBase
    {
        readonly IServiceScopeFactory _serviceScopeFactory;

        public ApiResourcesController(IServiceScopeFactory serviceScopeFactory)
        {
            _serviceScopeFactory = serviceScopeFactory;
        }

        public PagedResponse Get(int maxResult = 10, int skipCount = 0)
        {
            using (var serviceScope = _serviceScopeFactory.CreateScope())
            {
                var context = serviceScope.ServiceProvider.GetRequiredService<ConfigurationDbContext>();
                var query = context.ApiResources.AsNoTracking().Include(t => t.Scopes);
                var data = query.Skip(skipCount).Take(maxResult).ToList();
                var total = query.Count();

                data.ForEach(t => t.Scopes.ForEach(s => s.ApiResource = null));
                return PagedResponse.New(total, data);
            }
        }

        [HttpPost]
        public ResponseBase<List<ApiResource>> Post([FromForm] List<ApiResource> resources)
        {
            using (var serviceScope = _serviceScopeFactory.CreateScope())
            {
                var context = serviceScope.ServiceProvider.GetRequiredService<ConfigurationDbContext>();

                var newApiResourceNames = resources.Select(t => t.Name).ToList();

                var apiResources = context.ApiResources.AsNoTracking().Where(t => newApiResourceNames.Contains(t.Name)).ToList();

                if (apiResources.Count > 0)
                {
                    var existsNames = apiResources.Select(t => t.Name).ToList();
                    return new ResponseBase<List<ApiResource>>(false, $" 这些资源名已存在：{string.Join(',', existsNames)}");
                }

                context.ApiResources.AddRange(resources);
                context.SaveChanges();
                return new ResponseBase<List<ApiResource>>(resources);
            }
        }

        [HttpPut]
        public ResponseBase<List<ApiResource>> Put([FromForm] List<ApiResource> resources)
        {
            using (var serviceScope = _serviceScopeFactory.CreateScope())
            {
                var context = serviceScope.ServiceProvider.GetRequiredService<ConfigurationDbContext>();

                var apiResourceNames = resources.Select(t => t.Name).ToList();

                var apiResources = context.ApiResources.AsNoTracking().Where(t => apiResourceNames.Contains(t.Name)).ToList();

                var existsNames = resources.Where(t => apiResources.Any(s => s.Id != t.Id && t.Name == s.Name)).Select(t => t.Name)
                     .ToList();

                if (existsNames.Contains("IdentityServerApi"))
                {
                    return new ResponseBase<List<ApiResource>>(false, $"不能修改IdentityServerApi资源");
                }

                if (existsNames.Count > 0)
                {
                    return new ResponseBase<List<ApiResource>>(false, $" 这些资源名已存在：{string.Join(',', existsNames)}");
                }

                var ids = resources.Select(t => t.Id).ToList();
                apiResources = context.ApiResources.AsNoTracking().Where(t => ids.Contains(t.Id)).ToList();

                foreach (var apiResource in apiResources)
                {
                    var m = resources.FirstOrDefault(t => t.Id == apiResource.Id);
                    if (m == null) continue;
                    apiResource.Name = m.Name;
                    apiResource.Description = m.Description;
                    apiResource.Enabled = m.Enabled;

                    context.ApiResources.Update(apiResource);
                }

                context.SaveChanges();
                return new ResponseBase<List<ApiResource>>(resources);
            }
        }

        [HttpDelete]
        public ResponseBase<List<ApiResource>> Delete([FromForm] List<int> ids)
        {
            using (var serviceScope = _serviceScopeFactory.CreateScope())
            {
                var context = serviceScope.ServiceProvider.GetRequiredService<ConfigurationDbContext>();

                var resources = context.ApiResources.AsNoTracking().Where(t => ids.Contains(t.Id)).ToList();

                var deleteForbidden = resources.Where(t => new[] { "IdentityServerApi" }.Contains(t.Name)).Select(t => t.Name).ToList();

                if (deleteForbidden.Count > 0)
                    return new ResponseBase<List<ApiResource>>(false, $"以下资源不能被删除:{string.Join(',', deleteForbidden)}");


                context.ApiResources.RemoveRange(resources);
                context.SaveChanges();
                return new ResponseBase<List<ApiResource>>(resources);
            }
        }


        [HttpPut]
        [Route("Scopes")]
        public async Task<ResponseBase> SaveScopes([FromForm]List<string> scopeNames, int id)
        {
            using (var serviceScope = _serviceScopeFactory.CreateScope())
            {
                var context = serviceScope.ServiceProvider.GetRequiredService<ConfigurationDbContext>();

                var resource = context.ApiResources.AsNoTracking()
                                                   .Include(s => s.Scopes)
                                                   .FirstOrDefault(s => s.Id == id);
                if (resource == null) return new ResponseBase(false, "资源不存在");

                //- 新增
                foreach (var scopeName in scopeNames)
                {
                    var isOld = resource.Scopes.Any(s => s.Scope == scopeName);

                    if (isOld) continue;
                    resource.Scopes.Add(new ApiResourceScope { Scope = scopeName, ApiResourceId = resource.Id });
                }

                //- 删除
                var removingItems = resource.Scopes.Where(t => scopeNames.All(s => s != t.Scope)).ToList();

                removingItems.ForEach(t=> context.Entry(t).State = EntityState.Deleted);
                
                context.ApiResources.Update(resource);
                await context.SaveChangesAsync();
                resource.Scopes.ForEach(t => t.ApiResource = null);
                return new ResponseBase(resource);
            }
        }
    }
}
